package com.lxm.enigma.filter;

import com.lxm.enigma.algorithm.AesAlgorithm;
import com.lxm.enigma.algorithm.Md5Algorithm;
import com.lxm.enigma.annotations.CipherData;
import com.lxm.enigma.annotations.CipherSign;
import com.lxm.enigma.annotations.EncipherData;
import com.lxm.enigma.core.EnigmaConfig;
import com.lxm.enigma.jsonresult.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.Objects;

/**
 * @Author: Lys
 * @Date 2022/5/31
 * @Describe controller拦截器，处理response.write之前的逻辑
 **/
@Slf4j
@RestControllerAdvice// 可以具体指定扫描包路径，节省开支 (basePackages = "com.lxm")
public class EnigmaResponseAdvice implements ResponseBodyAdvice<Object> {

    private final EnigmaConfig enigmaConfig;
    private final AesAlgorithm aesAlgorithm;
    private final Md5Algorithm md5Algorithm;

    @Autowired
    public EnigmaResponseAdvice(EnigmaConfig enigmaConfig, AesAlgorithm aesAlgorithm, Md5Algorithm md5Algorithm) {
        this.enigmaConfig = enigmaConfig;
        this.aesAlgorithm = aesAlgorithm;
        this.md5Algorithm = md5Algorithm;
    }

    /**
     * 当返回类型是jsonResult时介入
     *
     * @param returnType
     * @param converterType
     * @return
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        var f = returnType.getParameterType().equals(JsonResult.class);
        ;
        log.info("response advice suport : {}", f);
        return f;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (Objects.isNull(body)) {
            return null;
        }
        if (!(body instanceof JsonResult)) {
            return body;
        }
        var resp = (JsonResult) body;
        if (resp.getCode() != 0) {
            return body;
        }
        boolean goEnc = returnType.hasMethodAnnotation(EncipherData.class) || returnType.hasMethodAnnotation(CipherData.class) || returnType.hasMethodAnnotation(CipherSign.class);
        if (!goEnc) {
            return body;
        }
        var withSign = Objects.nonNull(returnType.getMethodAnnotation(CipherSign.class));
        var withData = ObjectUtils.anyNotNull(returnType.getMethodAnnotation(EncipherData.class), returnType.getMethodAnnotation(CipherData.class));
        var data = resp.getData();
        if (withData) {
            // 只加密data里数据
            try {
                // 加密
                String cipher = aesAlgorithm.encipher(data, enigmaConfig.getKey());
                return JsonResult.json(resp.getCode(), resp.getMessage(), cipher);
            } catch (Exception e) {
                log.error("enigma advice error : ", e);
            }
        } else if (withSign) {
            // 验签方式
            if (Objects.nonNull(data)) {
                try {
                    String sign = md5Algorithm.encipher(data, enigmaConfig.getKey());//CryptoUtils.md5Hex(oriSign);
                    var json = JsonResult.json(resp.getCode(), resp.getMessage(), resp.getData());
                    json.setSign(sign);
                    return json;
                } catch (Exception e) {
                    log.warn("enigma-response-body-advice sign error ", e);
                    return resp;
                }
            }
        }
        return body;
    }
}
